<?php

namespace Constructor;

class Factory implements IFactory
{
    public function create( $class, array $aliases = array() )
    {
        if( is_null( $class ) OR ! is_string( $class ) ) throw new BaseValueException( 'class', $class, 'string' );
        
        if( isset( $aliases[$class] ) ) $class = $aliases[$class];
        if( ! class_exists( $class ) ) throw new FactoryUndefinedClassException( $class );
        
        $createReflection = function( $class )
        {
            return new \ReflectionClass( $class );
        };
        
        $reflection = $createReflection( strtolower( $class ) );
        if( $reflection->isInterface() OR $reflection->isAbstract() ) 
        { 
            throw new FactoryImplementationNotFoundException( $class ); 
        }
        
        $dependencies = array();
        $ctor = $reflection->getConstructor();
        if( $ctor )
        {
            foreach( $ctor->getParameters() as $parameter )
            {
                if( ! $parameter->isOptional() )
                {
                    $reflectionParam = $parameter->getClass();
                    if( ! $reflectionParam ) 
                    {
                        if( $parameter->isArray() ) $dependencies [] = array();
                        else $dependencies [] = $reflectionParam;
                    }
                    else
                    {
                        try
                        {
                            $dependencies [] = $this->create( $reflectionParam->getName(), $aliases );
                        }
                        catch( \Exception $e ) 
                        { 
                            throw new FactoryParameterFailureException( 
                                $parameter->getName(), $reflectionParam, $e->getMessage()
                            ); 
                        }
                    }
                }
            }
            
            return $reflection->newInstanceArgs( $dependencies );
        }
        
        return $reflection->newInstance();
    }
}
